package com.ruoyi.generator.controller;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.text.CharsetKit;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumnSql;
import com.ruoyi.generator.domain.dto.YunxinSql;
import com.ruoyi.generator.service.GenTableServiceImpl;
import com.ruoyi.generator.util.DataPermissionSqlUtil;
import com.ruoyi.generator.util.VelocityInitializer;
import com.ruoyi.generator.util.VelocityUtils;
import io.swagger.annotations.ApiParam;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.generator.domain.GenTableSql;
import com.ruoyi.generator.service.GenTableSqlService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.constant.HttpStatus;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

/**
 * 代码生成业务Controller
 *
 * @author 众智汇云研发部
 * @date 2021-11-23
 */

@Api(value = "代码生成业务管理", tags = "代码生成业务管理")
@RestController
@RequestMapping("/tool/gen/sql")
public class GenTableSqlController extends BaseController {

    @Autowired
    protected GenTableSqlService genTableSqlService;

    private static final Logger log = LoggerFactory.getLogger(GenTableSqlController.class);

    /**
     * 预览代码
     */
    @PreAuthorize("@ss.hasPermi('tool:gen:preview')")
    @PostMapping("/preview")
    public AjaxResult preview(@Validated @RequestBody YunxinSql yunxinSql) throws Exception
    {
        String operName = SecurityUtils.getUsername();
        String jiexisql = yunxinSql.getSql();
        // 获取表名
        List<String> tablenames = DataPermissionSqlUtil.getTableNames(jiexisql);
        // 获取表的详细信息
        List<GenTableSql> genTableSqls = genTableSqlService.insertTable(yunxinSql,tablenames,operName);
        // 获取字段详细信息
        List<GenTableColumnSql> genTableColumnSqls = genTableSqlService.insertColumn(tablenames, genTableSqls, jiexisql);
        Map<String,Object> velocityMap = new HashMap<>();
        velocityMap.put("packageName",yunxinSql.getPackageName());
        velocityMap.put("functionName",yunxinSql.getFunctionName());
        velocityMap.put("businessName",yunxinSql.getBusinessName());
        velocityMap.put("className",yunxinSql.getClassName());
        velocityMap.put("newClassName",DataPermissionSqlUtil.toLowerCaseFirstOne(yunxinSql.getClassName()));
        velocityMap.put("columns",genTableColumnSqls);
        velocityMap.put("sql",yunxinSql.getSql());
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        VelocityInitializer.initVelocity();
        VelocityContext context = DataPermissionSqlUtil.prepareContext(velocityMap);
        List<String> templates = DataPermissionSqlUtil.getTemplateList();
        Map<String, String> dataMap = new LinkedHashMap<>();
        for (String template : templates)
        {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, Constants.UTF8);
            tpl.merge(context, sw);
            dataMap.put(template, sw.toString());
        }
        return AjaxResult.success(dataMap);
    }

    /**
     * 生成代码
     */
    @PreAuthorize("@ss.hasPermi('tool:gen:code')")
    @Log(title = "代码生成", businessType = BusinessType.GENCODE)
    @PostMapping("/batchGenCode")
    public String batchGenCode(HttpServletResponse response,@Validated @RequestBody YunxinSql yunxinSql) throws Exception
    {
        String operName = SecurityUtils.getUsername();
        String jiexisql = yunxinSql.getSql();
        // 获取表名
        List<String> tablenames = DataPermissionSqlUtil.getTableNames(jiexisql);
        // 获取表的详细信息
        List<GenTableSql> genTableSqls = genTableSqlService.insertTable(yunxinSql,tablenames,operName);

        // 获取字段详细信息
        List<GenTableColumnSql> genTableColumnSqls = genTableSqlService.insertColumn(tablenames, genTableSqls, jiexisql);
        Map<String,Object> velocityMap = new HashMap<>();
        velocityMap.put("packageName",yunxinSql.getPackageName());
        velocityMap.put("functionName",yunxinSql.getFunctionName());
        velocityMap.put("businessName",yunxinSql.getBusinessName());
        velocityMap.put("className",yunxinSql.getClassName());
        velocityMap.put("newClassName",DataPermissionSqlUtil.toLowerCaseFirstOne(yunxinSql.getClassName()));
        velocityMap.put("columns",genTableColumnSqls);
        velocityMap.put("sql",yunxinSql.getSql());
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        VelocityInitializer.initVelocity();
        VelocityContext context = DataPermissionSqlUtil.prepareContext(velocityMap);
        List<String> templates = DataPermissionSqlUtil.getTemplateList();
        if("0".equals(yunxinSql.getGenType())){
            // 获取模板列表
            for (String template : templates)
            {
                // 渲染模板
                StringWriter sw = new StringWriter();
                Template tpl = Velocity.getTemplate(template, Constants.UTF8);
                tpl.merge(context, sw);
                try
                {
                    // 添加到zip
                    zip.putNextEntry(new ZipEntry(DataPermissionSqlUtil.getFileName(template,yunxinSql.getBusinessName(), yunxinSql.getPackageName(),yunxinSql.getClassName())));
                    IOUtils.write(sw.toString(), zip, Constants.UTF8);
                    IOUtils.closeQuietly(sw);
                    zip.flush();
                    zip.closeEntry();
                }
                catch (IOException e)
                {
                    log.error("渲染模板失败" , e);
                }
            }
            IOUtils.closeQuietly(zip);
            byte[] data = outputStream.toByteArray();
            genCode(response, data);
        } else {
            // 获取模板列表
            for (String template : templates)
            {
                if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm"))
                {
                    // 渲染模板
                    StringWriter sw = new StringWriter();
                    Template tpl = Velocity.getTemplate(template, Constants.UTF8);
                    tpl.merge(context, sw);
                    try
                    {
                        String path = getGenPath(yunxinSql, template);
                        System.out.println(path);
                        FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8);
                    }
                    catch (Exception e)
                    {
                        throw new CustomException("渲染模板失败");
                    }
                }
            }
        }
        return "生成成功";
    }

    /**
     * 获取代码生成地址
     *
     * @param table 业务表信息
     * @param template 模板文件路径
     * @return 生成地址
     */
    public static String getGenPath(YunxinSql yunxinSql, String template)
    {
        String genPath = yunxinSql.getGenPath();
        if (StringUtils.equals(genPath, "/") || "".equals(genPath) || genPath == null || genPath.indexOf("/") != -1)
        {
            String model = "";
            if(genPath.length() != 1){
                model = genPath.substring(genPath.indexOf("/"))+"/";
            }
            return System.getProperty("user.dir") + File.separator + model + "src" + File.separator +
                    DataPermissionSqlUtil.getFileName(template,yunxinSql.getBusinessName(), yunxinSql.getPackageName(),yunxinSql.getClassName());
        }
        return genPath + File.separator + DataPermissionSqlUtil.getFileName(template,yunxinSql.getBusinessName(), yunxinSql.getPackageName(),yunxinSql.getClassName());
    }
    /**
     * 生成zip文件
     */
    private void genCode(HttpServletResponse response, byte[] data) throws IOException
    {
        response.reset();
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\"");
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream; charset=UTF-8");
        IOUtils.write(data, response.getOutputStream());
    }

    /**
     * 运行sql
     */
    @PreAuthorize("@ss.hasPermi('tool:gen:list')")
    @Log(title = "运行sql", businessType = BusinessType.IMPORT)
    @GetMapping("/yunxinSql")
    public AjaxResult yunxinSql(@Validated YunxinSql yunxinSql)
    {
        int i = genTableSqlService.yunxinSql(yunxinSql);
        if(i == -1){
            return AjaxResult.error("sql错误无法运行");
        }
        return AjaxResult.success();
    }

    /**
     * 查询代码生成业务列表
     */
    @ApiOperation(value = "查询代码生成业务列表", notes = "根据代码生成业务进行筛选", tags = "CRUD")
    @PreAuthorize("@ss.hasPermi('generator:sql:list')")
    @GetMapping("/list")
    public TableDataInfo list(GenTableSql genTableSql) {
        startPage();
        List<GenTableSql> list = genTableSqlService.selectGenTableSqlList(genTableSql);
        return getDataTable(list);
    }

    /**
     * 获取代码生成业务详细信息
     */
    @ApiOperation(value = "获取代码生成业务详细信息", notes = "根据ID获取详情", tags = "CRUD")
    @PreAuthorize("@ss.hasPermi('generator:sql:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@ApiParam("编号") @PathVariable("id") Long id) {
        return AjaxResult.success(genTableSqlService.selectGenTableSqlById(id));
    }

    /**
     * 新增代码生成业务
     */
    @ApiOperation(value = "新增代码生成业务", response = HttpStatus.class,tags = "CRUD")
    @PreAuthorize("@ss.hasPermi('generator:sql:add')")
    @Log(title = "代码生成业务", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Valid @RequestBody GenTableSql genTableSql) {
        return toAjax(genTableSqlService.insertGenTableSql(genTableSql));
    }

    /**
     * 修改代码生成业务
     */
    @ApiOperation(value = "修改代码生成业务", response = HttpStatus.class,tags = "CRUD")
    @PreAuthorize("@ss.hasPermi('generator:sql:edit')")
    @Log(title = "代码生成业务", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Valid @RequestBody GenTableSql genTableSql) {
        return toAjax(genTableSqlService.updateGenTableSql(genTableSql));
    }

    /**
     * 删除代码生成业务
     */
    @ApiOperation(value = "删除代码生成业务", response = HttpStatus.class,tags = "CRUD")
    @PreAuthorize("@ss.hasPermi('generator:sql:remove')")
    @Log(title = "代码生成业务", businessType = BusinessType.DELETE)
	@DeleteMapping("/{ids}")
    public AjaxResult remove(@ApiParam("编号") @PathVariable Long[] ids) {
        return toAjax(genTableSqlService.deleteGenTableSqlByIds(ids));
    }

    /**
     * 导出代码生成业务列表
     */
    @ApiOperation(value="导出代码生成业务列表",response = HttpStatus.class)
    @PreAuthorize("@ss.hasPermi('generator:sql:export')")
    @Log(title = "代码生成业务", businessType = BusinessType.EXPORT)
    @GetMapping("/export")
    public AjaxResult export(GenTableSql genTableSql) {
        List<GenTableSql> list = genTableSqlService.selectGenTableSqlList(genTableSql);
        ExcelUtil<GenTableSql> util = new ExcelUtil<GenTableSql>(GenTableSql.class);
        return util.exportExcel(list, "sql");
    }
}
